package org.jing.ext.socket;

import org.jing.core.lang.JingException;
import org.jing.core.logger.JingLogger;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.Semaphore;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2024-07-08 <br>
 */
public abstract class BaseSocketServerOperator<T extends BaseSocketServerConfig> extends Thread {
    private T config;

    private Socket socket;

    private Semaphore semaphore;

    private boolean runnable = false;

    public final void init(T config, Socket socket, Semaphore semaphore) {
        this.config = config;
        this.socket = socket;
        this.semaphore = semaphore;
    }

    public final T getConfig() {
        return config;
    }

    public final Socket getSocket() {
        return socket;
    }

    public Semaphore getSemaphore() {
        return semaphore;
    }

    protected final JingLogger getLogger() {
        return JingLogger.getLogger(this.getClass());
    }

    protected void finalize() throws Throwable {
        super.finalize();
        if (null != socket && !socket.isClosed()) {
            try {
                socket.close();
            }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
                getLogger().error(t);
            }
        }
    }

    @Override public void run() {
        int hashCode = socket.hashCode();
        try {
            String ip = socket.getInetAddress().getHostAddress();
            int port = socket.getPort();
            String name = config.getName();
            JingLogger logger = getLogger();
            logger.info(">>>[{}] start, [ip: {}][port: {}][available: {}]", hashCode, ip, port, semaphore.availablePermits());
            logger.info(">>>[{}] name: {}", hashCode, name);
            boolean permission = condition();
            logger.info(">>>[{}] condition: {}", hashCode, permission);
            if (permission) {
                runnable = true;
                semaphore.acquire();
                InputStream reader = socket.getInputStream();
                OutputStream writer = socket.getOutputStream();
                logger.info(">>>[{}] operate...", hashCode);
                this.operate(reader, writer);
            }
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            getLogger().error(">>>[{}] failed to run", hashCode);
            getLogger().error(t);
            fail(t);
        }
        finally {
            release();
        }
    }

    protected abstract void operate(InputStream inputStream, OutputStream outputStream) throws Throwable;

    private void release() {
        JingLogger logger = getLogger();
        int hashCode = socket.hashCode();
        logger.info(">>>[{}] before release...", hashCode);
        beforeRelease();
        if (runnable) {
            this.semaphore.release();
        }
        if (null != socket && !socket.isClosed()) {
            try {
                logger.info(">>>[{}] releasing...", hashCode);
                socket.close();
            }
            catch (JingException e) {
                throw e;
            }
            catch (Throwable t) {
                getLogger().error(">>>[{}] failed to release", hashCode);
                getLogger().error(t);
            }
        }
        logger.info(">>>[{}] after release...", hashCode);
        afterRelease();
    }

    protected boolean condition() {
        return true;
    }

    protected void fail(Throwable t) {}

    protected void beforeRelease() {}

    protected void afterRelease() {}
}
